{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# V2 Model Server (SKLearn)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "> 2020-10-12 14:52:57,535 [warning] Failed resolving version info. Ignoring and using defaults\n"
     ]
    }
   ],
   "source": [
    "import mlrun"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Configuration and package dependencies"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "%nuclio: setting kind to 'serving'\n",
      "%nuclio: setting spec.build.baseImage to 'mlrun/mlrun'\n"
     ]
    }
   ],
   "source": [
    "%nuclio config kind=\"serving\"\n",
    "%nuclio config spec.build.baseImage = \"mlrun/mlrun\""
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [],
   "source": [
    "%%nuclio cmd -c\n",
    "python -m pip install numpy cloudpickle v3io sklearn"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Serving class code"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [],
   "source": [
    "from cloudpickle import load\n",
    "import numpy as np\n",
    "from typing import List"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [],
   "source": [
    "class ClassifierModel(mlrun.serving.V2ModelServer):\n",
    "    def load(self):\n",
    "        \"\"\"load and initialize the model and/or other elements\"\"\"\n",
    "        model_file, extra_data = self.get_model(\".pkl\")\n",
    "        self.model = load(open(model_file, \"rb\"))\n",
    "\n",
    "    def predict(self, body: dict) -> List:\n",
    "        \"\"\"Generate model predictions from sample.\"\"\"\n",
    "        feats = np.asarray(body[\"inputs\"])\n",
    "        result: np.ndarray = self.model.predict(feats)\n",
    "        return result.tolist()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [],
   "source": [
    "# mlrun: end-code"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Convert to function object"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The sklearn-project generated one or more models that will be deployed in the server project `sklearn-servers`"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [],
   "source": [
    "models_path = \"https://s3.wasabisys.com/iguazio/models/iris/model.pkl\""
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Document and save (as a template)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "> 2020-10-12 14:47:46,331 [info] function spec saved to path: function.yaml\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "<mlrun.runtimes.serving.ServingRuntime at 0x7f9da3564090>"
      ]
     },
     "execution_count": 16,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "fn = mlrun.code_to_function(\n",
    "    \"v2-model-server\",\n",
    "    description=\"generic sklearn model server\",\n",
    "    categories=[\"serving\", \"ml\"],\n",
    "    labels={\"author\": \"yaronh\", \"framework\": \"sklearn\"},\n",
    "    code_output=\".\",\n",
    ")\n",
    "fn.spec.default_class = \"ClassifierModel\"\n",
    "# print(fn.to_yaml())\n",
    "fn.export()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Configure and add model(s)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [],
   "source": [
    "fn.add_model(\"mymodel\", model_path=models_path)\n",
    "# fn.verbose = True"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Test models locally (using a server emulator)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# create an emulator (mock server) from the function configuration)\n",
    "server = fn.to_mock_server(globals())"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Test against the iris dataset "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {},
   "outputs": [],
   "source": [
    "from sklearn.datasets import load_iris\n",
    "\n",
    "iris = load_iris()\n",
    "x = iris[\"data\"].tolist()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "> 2020-10-12 14:44:19,297 [debug] router run model mymodel, op=infer\n",
      "> 2020-10-12 14:44:19,297 [debug] router run model mymodel, op=infer\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "dict_keys(['id', 'model_name', 'outputs'])"
      ]
     },
     "execution_count": 13,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "result = server.test(\"/v2/models/mymodel/infer\", {\"inputs\": x})\n",
    "result.keys()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "##  Deploy server"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "> 2020-10-12 14:44:20,662 [info] deploy started\n",
      "[nuclio] 2020-10-12 14:44:21,772 (info) Build complete\n",
      "[nuclio] 2020-10-12 14:44:29,852 (info) Function deploy complete\n",
      "[nuclio] 2020-10-12 14:44:29,859 done updating v2-srv-v2-model-server, function address: 3.128.234.166:30830\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "'http://3.128.234.166:30830'"
      ]
     },
     "execution_count": 14,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "fn.apply(mlrun.mount_v3io())\n",
    "fn.deploy(project=\"v2-srv\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "##  Test server"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "{'id': 'bd688fa9-38cc-4a5d-95e8-1445bdd1520a',\n",
       " 'model_name': 'mymodel',\n",
       " 'outputs': [0, 2]}"
      ]
     },
     "execution_count": 15,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "my_data = \"\"\"{\"inputs\":[[5.1, 3.5, 1.4, 0.2],[7.7, 3.8, 6.7, 2.2]]}\"\"\"\n",
    "fn.invoke(\"/v2/models/mymodel/infer\", my_data)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.7.6"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}
